package net.organizer.dao;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.persistence.Id;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.Transient;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.appengine.api.datastore.Entity;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.annotation.Parent;

public class Org {
	private static final Logger logger = Logger.getLogger(Org.class.getName());
	
	public static Key<Org> getKey(Domain domain, Long id){
		return new Key<Org>(domain.getKey(), Org.class, id);
	}
	protected String desc;
	@Transient protected Domain domain;
	@Parent protected Key<Domain> domainKey;
	@Id protected Long   id;
	protected String name;
	@Transient protected Org parentOrg;
	protected Key<Org> parentOrgKey;
	protected Long version = 1L;
	@Transient protected Collection<OrgPerson> orgPersons; 
	

	public Collection<OrgPerson> getOrgPersons() {
		return orgPersons;
	}

	public void setOrgPersons(Collection<OrgPerson> orgPersons) {
		this.orgPersons = orgPersons;
	}

	public Org() {
		super();
	}
	
	public Org(Org parentOrg, String desc, Domain domain, String name, String namespaceName) {
		super();
		this.domainKey = domain.getKey();
		this.parentOrg = parentOrg;
		if(this.parentOrg != null){
			this.parentOrgKey = parentOrg.getKey();
		}
		this.desc = desc;
		this.domainKey = domain.getKey();
		this.domain = domain;
		this.name = name;
	}
	public String getDesc() {
		return desc;
	}
	public Domain getDomain() {
		return domain;
	}
	public Key<Domain> getDomainKey() {
		return domainKey;
	}
	public Long getId() {
		return id;
	}
	/**
	 * returns key with domain parent entity reference
	 * @return
	 */
	public Key<Org> getKey(){
		if(this.domainKey==null){
			throw new UnsupportedOperationException(String.format("attempt to getKey with null instance domain... org: %s", this));
		}
		return new Key<Org>(domainKey, Org.class, this.getId());
	}
	public String getName() {
		return name;
	}
	public Org getParentOrg() {
		return parentOrg;
	}
	public Key<Org> getParentOrgKey() {
		return parentOrgKey;
	}
	public Long getVersion() {
		return version;
	}
	@PostLoad void postLoad(Objectify ofy, Entity entity){
		//logger.fine(String.format("postLoad... entity: %s", entity));
		this.verifyRequiredFields();
	}
	@PrePersist void prePersist(Objectify ofy, Entity entity){
		//logger.fine(String.format("prePersist... entity: %s", entity));
		this.verifyRequiredFields();
	}
	public void setDesc(String desc) {
		this.desc = desc;
	}
	public void setDomain(Domain domain) {
		this.domain = domain;
	}
	public void setDomainKey(Domain domain) {
		this.domainKey = domain.getKey();
	}
	public void setDomainKey(Key<Domain> domainKey) {
		this.domainKey = domainKey;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public void setName(String name) {
		this.name = name;
	}
	public void setParentOrg(Org parentOrg) {
		this.parentOrg = parentOrg;
		if(parentOrg == null){
			this.parentOrgKey = null;
		}else{
			this.parentOrgKey = parentOrg.getKey();
		}
	}
	public void setParentOrgKey(Key<Org> parentOrgKey) {
		this.parentOrgKey = parentOrgKey;
	}
	public void setVersion(Long version) {
		this.version = version;
	}
	public JSONObject toJSONObject() {
		  JSONObject result = new JSONObject();
		  try {
			result.put("desc", this.getDesc());
			result.put("id", this.getId());
			result.put("name", this.getName());
			result.put("version", this.getVersion());
			if(this.getParentOrg() !=null){
				result.put("parentOrg", this.getParentOrg().toJSONObject());
			}
			if(this.getDomain() != null){
				result.put("domain", this.getDomain().toJSONObject());
			}
			if(this.orgPersons != null){
				JSONArray orgPersonsJSONArray = new JSONArray();
				for(OrgPerson orgPerson : this.orgPersons){
					orgPersonsJSONArray.put(orgPerson.toJSONObject());
				}
				result.put("orgPersons", orgPersonsJSONArray);
			}
		} catch (JSONException e) {
			throw new RuntimeException(e);
		}
		return result;
	}
	@Override
	public String toString() {
		return "Org [parentOrgKey=" + parentOrgKey + ", desc=" + desc + ", domain=" + domain
				+ ", domainKey=" + domainKey + ", id=" + id + ", name=" + name
				+ ", version=" + version + "]";
	}
	protected void verifyRequiredFields(){
		//logger.fine(String.format("verifyRequired...%s", this));
		if(this.getDomainKey()==null){
			throw new RuntimeException(String.format("missing required field domainKey...%s", this));
		}
		if(this.getName()==null || this.getName().length()==0){
			throw new RuntimeException(String.format("missing required field name...%s", this));
		}
		if(this.getVersion()==null){
			throw new RuntimeException(String.format("missing required field version...%s", this));
		}
	}
	/**
	 * Retrieve the child persons, load into the persons member variable.
	 * @param ofy
	 */
	public void loadOrgPersons(Objectify ofy, Domain domain){
		List<OrgPerson> orgPersons  = ofy.query(OrgPerson.class).ancestor(domain.getKey()).filter("orgId = ", id).list();
		logger.fine("orgPersons.size(): " + orgPersons.size());
		this.orgPersons = orgPersons;

		List<Key<Person>> personKeys = new LinkedList<Key<Person>>();	
		for(OrgPerson orgPerson : orgPersons){
			personKeys.add(Person.getKey(domain, orgPerson.getPersonId()));
		}
		logger.fine("personKeys.size(): " + personKeys.size());
		for(Key<Person> personKey : personKeys){
			logger.fine(String.format("personKey id: %d, name: %s, raw: %s, root: %s",
					personKey.getId(), personKey.getName(), personKey.getRaw(), personKey.getRoot()));
		}
		Map<Key<Person>, Person> getResult = ofy.get(personKeys);
		Collection<Person> results = getResult.values();
		Person[] persons = results.toArray(new Person[results.size()]);
		logger.fine("persons.length: " + persons.length);
		int i = 0;
		for(OrgPerson orgPerson : orgPersons){
			orgPerson.setPerson(persons[i++]);
		}
	}
}
